/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package action.administration;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.validator.annotations.RequiredStringValidator;
import com.opensymphony.xwork2.validator.annotations.StringLengthFieldValidator;
import com.opensymphony.xwork2.validator.annotations.ValidatorType;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.URL;
import java.net.URLConnection;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang.StringUtils;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.interceptor.validation.SkipValidation;
import org.htmlparser.Node;
import org.htmlparser.Parser;
import org.htmlparser.Tag;
import org.htmlparser.filters.HasAttributeFilter;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.tags.Div;
import org.htmlparser.tags.HeadingTag;
import org.htmlparser.tags.ImageTag;
import org.htmlparser.tags.MetaTag;
import org.htmlparser.tags.TitleTag;
import org.htmlparser.util.NodeList;

/**
 *
 * @author Thai Ha Cong
 */
@Namespace("/Administration/Tools")
public class OnpageSEOCheckerAction extends ActionSupport {

    private String inputURL;
    private String keyword;
    private String rurl;
    private String commentUrl;
    private String rtitle;
    private String commentTitle;
    private String rmetaDescription;
    private String commentMetaDescription;
    private String rmetaKeyword;
    private String commentMetaKeyword;
    private String rh1;
    private String commentH1;
    private String rh2;
    private String commentH2;
    private String rh3;
    private String commentH3;
    private String rkeyCount;
    private String commentKeyCount;
    private String rnavigation;
    private String commentNavigation;
    private String rimageAlt;
    private String commentImageAlt;
    private String rimageTitle;
    private String commentImageTitle;

    public OnpageSEOCheckerAction() {
    }

    public String getInputURL() {
        return inputURL;
    }

    @RequiredStringValidator(type = ValidatorType.FIELD, message = "URL is required.")
    public void setInputURL(String inputURL) {
        this.inputURL = inputURL;
    }

    public String getKeyword() {
        return keyword;
    }

    @RequiredStringValidator(type = ValidatorType.FIELD, message = "Keyword is required.")
    @StringLengthFieldValidator(type = ValidatorType.FIELD, message = "Keyword should be from 3 to 30 chracters.", minLength = "3", maxLength = "30")
    public void setKeyword(String keyword) {
        this.keyword = keyword;
    }

    public String getRurl() {
        return rurl;
    }

    public void setRurl(String rurl) {
        this.rurl = rurl;
    }

    public String getRtitle() {
        return rtitle;
    }

    public void setRtitle(String rtitle) {
        this.rtitle = rtitle;
    }

    public String getRmetaDescription() {
        return rmetaDescription;
    }

    public void setRmetaDescription(String rmetaDescription) {
        this.rmetaDescription = rmetaDescription;
    }

    public String getRmetaKeyword() {
        return rmetaKeyword;
    }

    public void setRmetaKeyword(String rmetaKeyword) {
        this.rmetaKeyword = rmetaKeyword;
    }

    public String getRh1() {
        return rh1;
    }

    public void setRh1(String rh1) {
        this.rh1 = rh1;
    }

    public String getRh2() {
        return rh2;
    }

    public void setRh2(String rh2) {
        this.rh2 = rh2;
    }

    public String getRh3() {
        return rh3;
    }

    public void setRh3(String rh3) {
        this.rh3 = rh3;
    }

    public String getRkeyCount() {
        return rkeyCount;
    }

    public void setRkeyCount(String rkeyCount) {
        this.rkeyCount = rkeyCount;
    }

    public String getRnavigation() {
        return rnavigation;
    }

    public void setRnavigation(String rnavigation) {
        this.rnavigation = rnavigation;
    }

    public String getRimageAlt() {
        return rimageAlt;
    }

    public void setRimageAlt(String rimageAlt) {
        this.rimageAlt = rimageAlt;
    }

    public String getRimageTitle() {
        return rimageTitle;
    }

    public void setRimageTitle(String rimageTitle) {
        this.rimageTitle = rimageTitle;
    }

    public String getCommentUrl() {
        return commentUrl;
    }

    public void setCommentUrl(String commentUrl) {
        this.commentUrl = commentUrl;
    }

    public String getCommentTitle() {
        return commentTitle;
    }

    public void setCommentTitle(String commentTitle) {
        this.commentTitle = commentTitle;
    }

    public String getCommentMetaDescription() {
        return commentMetaDescription;
    }

    public void setCommentMetaDescription(String commentMetaDescription) {
        this.commentMetaDescription = commentMetaDescription;
    }

    public String getCommentMetaKeyword() {
        return commentMetaKeyword;
    }

    public void setCommentMetaKeyword(String commentMetaKeyword) {
        this.commentMetaKeyword = commentMetaKeyword;
    }

    public String getCommentH1() {
        return commentH1;
    }

    public void setCommentH1(String commentH1) {
        this.commentH1 = commentH1;
    }

    public String getCommentH2() {
        return commentH2;
    }

    public void setCommentH2(String commentH2) {
        this.commentH2 = commentH2;
    }

    public String getCommentH3() {
        return commentH3;
    }

    public void setCommentH3(String commentH3) {
        this.commentH3 = commentH3;
    }

    public String getCommentKeyCount() {
        return commentKeyCount;
    }

    public void setCommentKeyCount(String commentKeyCount) {
        this.commentKeyCount = commentKeyCount;
    }

    public String getCommentNavigation() {
        return commentNavigation;
    }

    public void setCommentNavigation(String commentNavigation) {
        this.commentNavigation = commentNavigation;
    }

    public String getCommentImageAlt() {
        return commentImageAlt;
    }

    public void setCommentImageAlt(String commentImageAlt) {
        this.commentImageAlt = commentImageAlt;
    }

    public String getCommentImageTitle() {
        return commentImageTitle;
    }

    public void setCommentImageTitle(String commentImageTitle) {
        this.commentImageTitle = commentImageTitle;
    }

    @Override
    public String execute() throws Exception {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Action(value = "onpageSEOCheckerResult", results = {
        @Result(name = "success", location = "/administration/seoCheckerResult.jsp"),
        @Result(name = "error", location = "/administration/seoChecker.jsp"),
        @Result(name = "input", location = "/administration/seoChecker.jsp")
    })
    public String checkingResult() {
        try {
            //get cookies at 1st time
            URL url = new URL(inputURL);

            CookieManager manager = new CookieManager();
            manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
            CookieHandler.setDefault(manager);

            URLConnection connection = url.openConnection();
            connection.getContent();

            //check at 2nd time
            URL url2 = new URL(inputURL);

            URLConnection connection2 = url2.openConnection();
            BufferedReader br = new BufferedReader(new InputStreamReader(connection2.getInputStream()));

            String htmlCode = "";
            while (br.readLine() != null) {
                htmlCode = htmlCode + br.readLine();
            }
            //System.out.println(htmlCode);

            Parser parser = new Parser();

            keyword = keyword.toLowerCase().replaceAll("-", " ");

            //url
            this.checkUrl();

            //title
            TagNameFilter titleFilter = new TagNameFilter("title");
            parser.setConnection(url2.openConnection());
            NodeList titleNodeList = parser.parse(titleFilter);
            this.checkTitle(titleNodeList);

            //meta Description
            HasAttributeFilter metaDesFilter = new HasAttributeFilter("name", "description");
            parser.setConnection(url2.openConnection());
            NodeList metaDesNodeList = parser.parse(metaDesFilter);
            this.checkMetaDescription(metaDesNodeList);

            //meta Keyword
            HasAttributeFilter metaKeyFilter = new HasAttributeFilter("name", "keyword");
            parser.setConnection(url2.openConnection());
            NodeList metaKeyNodeList = parser.parse(metaKeyFilter);
            this.checkMetaKeyword(metaKeyNodeList);

            //heading
            NodeClassFilter headingClassFilter = new NodeClassFilter(Class.forName("org.htmlparser.tags.HeadingTag"));
            parser.setConnection(url2.openConnection());
            NodeList headingNodeList = parser.parse(headingClassFilter);
            this.checkHeading(headingNodeList);

            //total Key Count
            this.checkKeyCount(htmlCode);

            //check navigation
            HasAttributeFilter breadcrumbAttributeFilter = new HasAttributeFilter("class", "breadcrumb");
            HasAttributeFilter navigationAttributeFilter = new HasAttributeFilter("class", "navigation");
            parser.setConnection(url2.openConnection());
            NodeList breadcrumbNodeList = parser.parse(breadcrumbAttributeFilter);
            parser.setConnection(url2.openConnection());
            NodeList navigationNodeList = parser.parse(navigationAttributeFilter);
            this.checkNavigation(breadcrumbNodeList, navigationNodeList);

            //check image
            TagNameFilter imgFilter = new TagNameFilter("img");
            parser.setConnection(url2.openConnection());
            NodeList imgNodeList = parser.parse(imgFilter);
            this.checkImg(imgNodeList);

            return SUCCESS;
        } catch (Exception ex) {
            Logger.getLogger(OnpageSEOCheckerAction.class.getName()).log(Level.SEVERE, null, ex);

            addActionError("Wrong URL!");
            return ERROR;
        }
    }

    @Action(value = "onpageSEOChecker", results = {
        @Result(name = "success", location = "/administration/seoChecker.jsp")
    })
    @SkipValidation
    public String checking() {
        return SUCCESS;
    }

    private void checkUrl() {
        int c = StringUtils.countMatches(inputURL.toLowerCase().replaceAll("-", " "), keyword);

        rurl = c + " time(s)";

        if (c > 0) {
            commentUrl = "Good URL.";
        } else {
            commentUrl = "Poor URL.";
        }
    }

    private void checkTitle(NodeList nodes) {
        int c = 0;
        int t = 0;

        if (nodes != null) {
            //System.out.println(nodes.size());
            for (Node node : nodes.toNodeArray()) {
                if (node == null) {
                    continue;
                }

                if (node instanceof TitleTag) {
                    t++;
                    TitleTag titleTag = (TitleTag) node;

                    c = StringUtils.countMatches(titleTag.getStringText().toLowerCase().replaceAll("-", " "), keyword);
                }
            }
        }

        if (t == 0) {
            rtitle = "Not Found";
            commentTitle = "<title> tag is not found.";
        } else {
            rtitle = c + " of " + t + " time(s)";

            if (c > 0) {
                commentTitle = "Good Title.";
            } else {
                commentTitle = "Poor Title.";
            }
        }
    }

    private void checkMetaDescription(NodeList nodes) {
        int c = 0;
        int t = 0;

        if (nodes != null) {
            for (Node node : nodes.toNodeArray()) {
                if (node == null) {
                    continue;
                }

                if (node instanceof MetaTag) {
                    t++;
                    MetaTag metaTag = (MetaTag) node;

                    c = StringUtils.countMatches(metaTag.getAttribute("content").toLowerCase(), keyword);
                }
            }
        }

        if (t == 0) {
            rmetaDescription = "Not Found";
            commentMetaDescription = "'description' attribute is not found in <meta> tag.";
        } else {
            rmetaDescription = c + " of " + t + " time(s)";

            if (c > 0) {
                commentMetaDescription = "Good Description Meta tag.";
            } else {
                commentMetaDescription = "Bad Description Meta tag.";
            }
        }
    }

    private void checkMetaKeyword(NodeList nodes) {
        int c = 0;
        int t = 0;

        if (nodes != null) {
            for (Node node : nodes.toNodeArray()) {
                if (node == null) {
                    continue;
                }

                if (node instanceof MetaTag) {
                    t++;
                    MetaTag metaTag = (MetaTag) node;

                    c = StringUtils.countMatches(metaTag.getAttribute("content").toLowerCase(), keyword);
                }
            }
        }

        if (t == 0) {
            rmetaKeyword = "Not Found";
            commentMetaKeyword = "'keyword' attribute is not found in <meta> tag.";
        } else {
            rmetaKeyword = c + " of " + t + " time(s)";

            if (c > 0) {
                commentMetaKeyword = "Good Keyword Meta tag.";
            } else {
                commentMetaKeyword = "Bad Keyword Meta tag.";
            }
        }
    }

    private void checkHeading(NodeList nodes) {
        int c1 = 0;
        int c2 = 0;
        int c3 = 0;

        int h1 = 0;
        int h2 = 0;
        int h3 = 0;

        if (nodes != null) {
            for (Node node : nodes.toNodeArray()) {
                if (node == null) {
                    continue;
                }

                if (node instanceof HeadingTag) {
                    HeadingTag headingTag = (HeadingTag) node;
                    String type = headingTag.getRawTagName();
                    String content = headingTag.getStringText();

                    if (type.equals("h1")) {
                        h1++;
                        c1 = c1 + StringUtils.countMatches(content.toLowerCase(), keyword);
                    }
                    if (type.equals("h2")) {
                        h2++;
                        c2 = c2 + StringUtils.countMatches(content.toLowerCase(), keyword);
                    }
                    if (type.equals("h3")) {
                        h3++;
                        c3 = c3 + StringUtils.countMatches(content.toLowerCase(), keyword);
                    }
                }
            }
        }

        if (h1 == 0) {
            rh1 = "Not Found";
            commentH1 = "<h1> tag is not found.";
        } else {
            rh1 = c1 + " of " + h1 + " time(s)";

            if (c1 > 0) {
                commentH1 = "Good Heading 1 tag.";
            } else {
                commentH1 = "Heading 1 tag should contain Keyword.";
            }
        }

        if (h2 == 0) {
            rh2 = "Not Found";
            commentH2 = "<h2> tag is not found.";
        } else {
            rh2 = c2 + " of " + h2 + " time(s)";

            if (c2 > 0) {
                commentH2 = "Good Heading 2 tag.";
            } else {
                commentH2 = "Heading 2 tag should contain Keyword.";
            }
        }

        if (h3 == 0) {
            rh3 = "Not Found";
            commentH3 = "<h3> tag is not found.";
        } else {
            rh3 = c3 + " of " + h3 + " time(s)";

            if (c3 > 0) {
                commentH3 = "Good Heading 3 tag.";
            } else {
                commentH3 = "Heading 3 tag should contain Keyword.";
            }
        }
    }

    private void checkKeyCount(String htmlCode) {
        int c = StringUtils.countMatches(htmlCode.toLowerCase().replaceAll("-", " "), keyword);

        rkeyCount = (c == 0) ? "Not Found" : (c + " time(s)");
    }

    private void checkNavigation(NodeList nodes, NodeList nodes2) {
        int t1 = 0;
        int t2 = 0;

        if (nodes != null) {
            for (Node node : nodes.toNodeArray()) {
                if (node == null) {
                    continue;
                }

                if (node instanceof Div) {
                    t1++;
                }
            }
        }

        if (nodes2 != null) {
            for (Node node : nodes2.toNodeArray()) {
                if (node == null) {
                    continue;
                }

                if (node instanceof Div) {
                    t2++;
                }
            }
        }

        if (t1 == 0) {
            if (t2 == 0) {
                rnavigation = "Not Found";
                commentNavigation = "Breadcrumb Div or Navigation Div is not found.";
            } else {
                rnavigation = "Yes";
                commentNavigation = "Navigation Div exists. Maybe good navigation.";
            }
        } else {
            rnavigation = "Yes";
            commentNavigation = "Breadcrumb Div exists. Maybe good navigation.";
        }
    }

    private void checkImg(NodeList nodes) {
        int calt = 0;
        int ctitle = 0;
        int cimg = 0;

        if (nodes == null) {
        } else {
            for (Node node : nodes.toNodeArray()) {
                if (node == null) {
                    continue;
                }

                //System.out.println(node.getClass());

                if (node instanceof ImageTag) {
                    cimg++;
                    ImageTag img = (ImageTag) node;

                    if (img.getAttribute("alt") != null) {
                        calt++;
                    }
                    if (img.getAttribute("title") != null) {
                        ctitle++;
                    }

                    Tag parent = (Tag) img.getParent();
                    if ("productImage".equals(parent.getAttribute("class"))) {
                        System.out.println("Product Image exists.");
                    }
                }
            }
        }

        if (cimg == 0) {
            rimageAlt = "Not Found";
            rimageTitle = "Not Found";
        } else {
            rimageAlt = calt + " of " + cimg + " time(s)";

            if (calt == cimg) {
                commentImageAlt = "Very Good Result.";
            } else if ((double) calt / cimg * 100 >= 50) {
                commentImageAlt = "Good Result.";
            } else if ((double) calt / cimg * 100 > 0) {
                commentImageAlt = "Bad Result. You should add alt tag for left image(s) on the page.";
            } else {
                commentImageAlt = "Very Bad Result. You should add alt tag for image(s) on the page.";
            }

            rimageTitle = ctitle + " of " + cimg + " time(s)";

            if (ctitle == cimg) {
                commentImageTitle = "Very Good Result.";
            } else if ((double) ctitle / cimg * 100 >= 50) {
                commentImageTitle = "Good Result.";
            } else if ((double) ctitle / cimg * 100 > 0) {
                commentImageTitle = "Bad Result. You should add title tag for left image(s) on the page "
                        + "to give users brief information about the image(s)."
                        + "<br>//Image(s) of third parties, such as Yahoo, can be ignored.";
            } else {
                commentImageTitle = "Very Bad Result. You should add title tag for image(s) on the page "
                        + "to give users brief information about the image(s)."
                        + "<br>//Image(s) of third parties, such as Yahoo, can be ignored.";
            }
        }
    }
}